﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace B121 {
    public class RecursiveBestFit : PackAlg {
        /// <summary>
        /// Attemps to pack the containers
        /// </summary>
        /// <returns>0 if all items was packed, otherwise false</returns>
        protected override PackingReturnValues AttempToPack() {
            foreach (ContainerClass container in Containers) {
                PackAContainer(container); // Pack all containers
            }

            return (Items.Count > 0 ? PackingReturnValues.ItemDoesNotFit : 0); // Any items left?
        }

        /// <summary>
        /// Pack a single container
        /// </summary>
        /// <param name="container">The container to pack</param>
        private void PackAContainer(ContainerClass container) {
            int width = container.X, height = container.Y;
            int OffsetX = 0;

            while (DoesAnyItemFit(width, height, container.MaxWeight - container.CurrentWeight)) {
                OffsetX = 0; // Reset Offset X
                foreach (ItemClass item in container.Items) {
                    if (item.OffsetY == 0) {
                        OffsetX += item.X; // Calculate Offset X
                    }
                }
                width = container.X - OffsetX; // Calculate width remaining

                CreateAStack(container, width, height, OffsetX, 0);
            }
        }

        /// <summary>
        /// Checks if an item fits a specified width, height and weight
        /// </summary>
        /// <param name="width">Max width of item</param>
        /// <param name="height">Max height of item</param>
        /// <param name="weight">Max weight of item</param>
        /// <returns>True if an item fits, otherwise false</returns>
        private bool DoesAnyItemFit(int width, int height, int weight) {
            foreach (ItemClass item in Items) {
                if (item.X <= width && item.Y <= height && item.Weight <= weight) {
                    return true; // At least one item fits
                }
            }
            return false; // No items fit
        }

        /// <summary>
        /// Creates a stack (if any items fit the criteria)
        /// </summary>
        /// <param name="container">Container to create the stack in</param>
        /// <param name="maxWidth">Max width of the stack</param>
        /// <param name="maxHeight">Max height of the stack</param>
        /// <param name="offsetX">Offset X of the stack</param>
        /// <param name="offsetY">Offset Y of the stack</param>
        private void CreateAStack(ContainerClass container, int maxWidth, int maxHeight, int offsetX, int offsetY) {
            ItemClass itemToPlace = null; // Assume not items fit

            foreach (ItemClass item in Items) {
                if (item.X <= maxWidth && item.Y <= maxHeight && item.Weight + container.CurrentWeight <= container.MaxWeight) { // weight
                    itemToPlace = item; // This item fits
                    break;
                }
            }

            if (itemToPlace == null) { return; } // No items fit?

            PlaceItem(container, itemToPlace, offsetX, offsetY); // Place the item

            CreateAStack(container, itemToPlace.X, maxHeight - itemToPlace.Y, offsetX, offsetY + itemToPlace.Y); // Create a stack on top of the current one

            CreateAStack(container, maxWidth - itemToPlace.X, maxHeight, offsetX + itemToPlace.X, offsetY); // Create a stack next to the one just created
        }

        /// <summary>
        /// Places an item in a container, and removes the item from the list of items
        /// </summary>
        /// <param name="container">Container to place the item in</param>
        /// <param name="item">Item to place</param>
        /// <param name="offsetX">Offset X of the item</param>
        /// <param name="offsetY">Offset Y of the item</param>
        private void PlaceItem(ContainerClass container, ItemClass item, int offsetX, int offsetY) {
            item.OffsetX = offsetX;
            item.OffsetY = offsetY;
            container.Items.Add(item);

            Items.Remove(item);
        }
    }
}
